// Physical memory layout

// qemu -machine virt is set up like this, based on qemu's hw/riscv/virt.c:
//
// 00001000 -- boot ROM, provided by qemu
// 02000000 -- CLINT
// 0C000000 -- PLIC
// 10000000 -- uart0
// 10001000 -- virtio disk
// 80000000 -- boot ROM jumps here in machine mode, -kernel loads the kernel here
// unused RAM after 80000000.

// the kernel uses physical memory thus:
// 80000000 -- entry.S, then kernel text and data
// end -- start of kernel page allocation area
// PHYSTOP -- end RAM used by the kernel
const K = 1024;
const M = 1024 * K;
pub const KERNBASE = 0x8000_0000;
pub const TOTAL_BYTES = 128 * M;
pub const PHYSTOP = KERNBASE + TOTAL_BYTES;

pub const UART0_IRQ = 10;
pub const VIRTIO0_IRQ = 1;

// PCI-E ECAM (configuration space)
pub const PCIE_ECAM = 0x3000_0000;
pub const PCIE_MMIO = 0x4000_0000;
pub const PCIE_IRQ = 32; // 32 to 35
pub const PCIE_IRQ_COUNT = 4;
// e1000's registers
pub const E1000 = PCIE_MMIO;

/// Timer interrupt interval (~100ms)
pub const TIMER_TICK_INTERVAL = 100_0000;

pub const MemMapEntry = struct { usize, usize };

pub const memmap = [_]MemMapEntry{
    .{ PCIE_MMIO, 0x2_0000 },
};

pub fn init() void {
    // Initialize physical page allocator for early boot requirements
    const fdt_addr = riscv.gpr.read(.gp);
    const fdt_addr_aligned = std.mem.alignBackward(usize, fdt_addr, os.PGSIZE);
    PageAllocator.init(fdt_addr_aligned);

    // Parse device tree to discover system hardware layout
    os.fdt.init(fdt_addr) catch unreachable;

    // Set the number of CPUs
    os.proc.ncpus = os.fdt.ncpus();
    std.debug.assert(os.proc.ncpus > 0);

    if (!options.m_mode) {
        // Start all CPU cores with the entry point and FDT address
        for (0..os.proc.ncpus) |i| {
            riscv.sbi.hartStart(i, @ptrCast(&start._entry), fdt_addr) catch {};
        }
    }

    // Initialize UART for early console output
    const uart_node = os.fdt.stdout().?;
    os.driver.call(uart_node) catch unreachable;

    // Set up interrupt controller
    const ic_node = os.fdt.intc().?;
    os.driver.call(ic_node) catch unreachable;
}

const std = @import("std");
const riscv = @import("riscv");
const options = @import("options");
const os = @import("../os.zig");
const start = @import("../start.zig");
const PageAllocator = os.mem.PageAllocator;
